
#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <ctime>
#include <cstdlib>
using namespace std;
#include "task.hpp"

enum
{
    UsageError = 1,
    ArgError,
    PipeError
};

void Usage(const std::string &proc)
{
    cout << "Usage: " << proc << " sub_process_num " << endl;
}

class Channel
{
public:
    Channel(int wfd, pid_t sub_process_id, const std::string &name)
        : _wfd(wfd), _sub_process_id(sub_process_id), _name(name)
    {
    }

    void PrintDebug()
    {
        cout << "_wfd: " << _wfd;
        cout << ",_sub_process_id: " << _sub_process_id;
        cout << ",_name: " << _name << endl;
    }

    ~Channel()
    {
    }
    string name() { return _name; }
    int wfd() { return _wfd; }
    pid_t pid() { return _sub_process_id; }

private:
    int _wfd;
    pid_t _sub_process_id;
    string _name;
};

class ProcessPool
{
public:
    ProcessPool(int sub_process_num)
        : _sub_process_num(sub_process_num)
    {
    }
    int CreateProcess(work_t work)
    {
        for (int number = 0; number < _sub_process_num; number++)
        {
            int pipefd[2] = {0};
            int n = pipe(pipefd);
            if (n < 0)
            {
                return PipeError;
            }

            pid_t id = fork();
            if (id == 0)
            {
                // child-> r
                close(pipefd[1]);

                // TODO
                dup2(pipefd[0], 0);
                work(pipefd[0]);

                // sleep(100);
                exit(0);
            }

            string cname = "channle-" + to_string(number);

            // father-> w
            close(pipefd[0]);

            _channels.push_back(Channel(pipefd[1], id, cname));
        }
        return 0;
    }

    int NextChannel()
    {
        static int next = 0;
        int c = next;
        next++;
        next %= _channels.size();
        return c;
    }

    void SendTaskCode(int index, uint32_t code)
    {
        //cout << "send code: " << code << " to " << _channels[index].name() << " sub process id: " << _channels[index].name() << endl;
        write(_channels[index].wfd(), &code, sizeof(code));
    }

    void Debug()
    {
        for (auto &channel : _channels)
        {
            channel.PrintDebug();
            // cout<<endl;
        }
    }

    ~ProcessPool()
    {
    }

private:
    int _sub_process_num;
    vector<Channel> _channels;
};

void CtrlProcessPool(ProcessPool *processpool_ptr, int cnt)
{
    while (cnt--)
    {
        // 1.选择一个进程和通道
        int channel = processpool_ptr->NextChannel();
        // sleep(1);
        // cout << channel.name() << endl;
        // 2.你要选择的一个任务
        uint32_t code = NextTask();
        sleep(1);
        // 3.发送任务
        processpool_ptr->SendTaskCode(channel, code);
    }
}

// ./processpool 5
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        Usage(argv[0]);
        return UsageError;
    }
    int sub_process_num = std::stoi(argv[1]);
    if (sub_process_num <= 0)
        return ArgError;

    srand((uint64_t)time(nullptr));
    //     vector <Channel> channels;
    //     //1.创建通信通道和子进程 -- bug
    //     for(int number = 0; number < sub_process_num ;number++)
    //     {
    //         int pipefd[2]={0};
    //         int n = pipe(pipefd);
    //         if(n<0)
    //         {
    //             return 2;
    //         }

    //         pid_t id = fork();

    //         if(id == 0)
    //         {
    //               //child->r
    //             close(pipefd[1]);

    //             sleep(100);
    //             exit(0);
    //         }
    //         string cname = "channel-" +  to_string(number);
    //         //father->w
    //         close(pipefd[0]);

    //         channels.push_back(Channel(pipefd[1],id,cname));
    //     }

    //    // 2.控制子进程
    //     for(auto& channel : channels)
    //     {
    //         channel.PrintDebug();
    //     }

    // 1.创建通信通道和子进程 -- bug
    ProcessPool *processpool_ptr = new ProcessPool(sub_process_num);
    processpool_ptr->CreateProcess(worker);
    //processpool_ptr->Debug();

    // 2.控制子进程
    CtrlProcessPool(processpool_ptr,10);

    sleep(100);

    delete processpool_ptr;
    return 0;
}